/**   
* @Title: AbstractThread.java
* @Package org.xaoyaoyao.spider.parse
* @Description: TODO
* @author xaoyaoyao
* @date Jan 25, 2019 1:43:30 PM
* @version V1.0   
*/
package org.xaoyaoyao.spider.parse;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.util.Assert;
import org.xaoyaoyao.spider.dto.DownloadFileDto;
import org.xaoyaoyao.spider.entity.SpiderSeed;
import org.xaoyaoyao.spider.enums.ResultEnum;
import org.xaoyaoyao.spider.init.SpiderSeedInitializationData;
import org.xaoyaoyao.spider.parser.XmlParser;
import org.xaoyaoyao.spider.processor.AbstractProcessor;
import org.xaoyaoyao.spider.properties.SpiderConfiguration;
import org.xaoyaoyao.spider.redis.RedisService;
import org.xaoyaoyao.spider.util.Constants;
import org.xaoyaoyao.spider.util.ContextLoader;
import org.xaoyaoyao.spider.util.StopSignalReceiver;

import lombok.extern.slf4j.Slf4j;

/**
 * @ClassName: AbstractThread
 * @Description: TODO
 * @author xaoyaoyao
 * @date Jan 25, 2019 1:43:30 PM
 * 
 */
@Slf4j
public abstract class AbstractThread<T> implements Runnable {

	private AbstractProcessor<T> processor;

	private boolean async;

	private Class<T> clazz;

	private RedisService redisService;

	private SpiderConfiguration spiderConfiguration;

	private long time = 30000;

	public AbstractThread(AbstractProcessor<T> processor, RedisService redisService, boolean async, Class<T> clazz) {
		super();
		Assert.notNull(processor, "AbstractProcessor cannot be null.");
		Assert.notNull(redisService, "RedisService cannot be null.");
		Assert.notNull(clazz, "Class<T> cannot be null.");
		this.processor = processor;
		this.redisService = redisService;
		this.async = async;
		this.clazz = clazz;
		this.spiderConfiguration = ContextLoader.getBean(SpiderConfiguration.class);
		Assert.notNull(spiderConfiguration, "SpiderConfiguration cannot be null.");
	}

	@Override
	public void run() {
		log.error(">>>>>>>> 开始执行任务...");
		this.time = spiderConfiguration.getParseInterval() <= 5 ? time : spiderConfiguration.getParseInterval() * time;
		while (!isExit()) {
			try {
				DownloadFileDto dto = getDownloadFileDto();
				if (null == dto) {
					Thread.sleep(time);// 30s
					continue;
				}
				String htmlTxt = dto.getHtmlTxt();
				log.debug(">>>>>>>> htmlTxt >> {}", htmlTxt);
				List<SpiderSeed> seeds = getSpiderSeeds().get(dto.getDomain());
				if (StringUtils.isNoneBlank(htmlTxt) && CollectionUtils.isNotEmpty(seeds)) {
					seeds.stream().forEach(seed -> {// 处理结果
						T result = XmlParser.builder().htmlTxt(htmlTxt).xslt(seed.getTemplate()).build().parse(clazz);
						if (null != result) {
							handle(result);
							return;
						}
					});
				}
			} catch (Exception e) {
				log.error(">>>>>>>> Check Thread Exception!", e);
			}
		}
	}

	private ResultEnum handle(T result) {
		if (async) { // 异步
			Future<ResultEnum> future = processor.asyncProcess(result);
			log.info(">>>>>>>> 执行结果 >> {}", future);
			try {
				return future.get();
			} catch (InterruptedException e) {
				log.error(e.getMessage(), e);
			} catch (ExecutionException e) {
				log.error(e.getMessage(), e);
			}
			return ResultEnum.ERROR;
		}
		ResultEnum resultEnum = processor.process(result);
		log.info(">>>>>>>> 执行结果 >> {}", resultEnum);
		return resultEnum;
	}

	private Map<String, List<SpiderSeed>> getSpiderSeeds() {
		try {
			return SpiderSeedInitializationData.getSpiderSeeds();
		} catch (Exception e) {
			log.error(e.getMessage(), e);
			return new HashMap<String, List<SpiderSeed>>(1);
		}
	}

	private DownloadFileDto getDownloadFileDto() {
		try {
			// 获取任务，可从redis获取任务
			String json = redisService.rpop(Constants.SPIDER_PARSER_KEYS);
			log.debug(">>>>>>>> DownloadFileDto >> {}", json);
			if (StringUtils.isNotBlank(json)) {
				return DownloadFileDto.parse(json);
			}
		} catch (Exception e) {
			log.error(e.getMessage(), e);
		}
		return null;
	}

	private boolean isExit() {
		return StopSignalReceiver.isReceived();
	}
}
